package cn.wizzer.app.user.modules.service.impl;

import cn.hutool.core.net.URLDecoder;
import cn.wizzer.app.sys.modules.models.*;
import cn.wizzer.app.sys.modules.models.constant.CurrencyConstant;
import cn.wizzer.app.sys.modules.services.*;
import cn.wizzer.app.user.modules.models.User;
import cn.wizzer.app.user.modules.models.UserBankCard;
import cn.wizzer.app.user.modules.models.UserWithdraw;
import cn.wizzer.app.user.modules.models.constant.GlobalPayParamConstant;
import cn.wizzer.app.user.modules.models.constant.PayNotifyConstant;
import cn.wizzer.app.user.modules.models.dto.ElephantWithdrawDTO;
import cn.wizzer.app.user.modules.models.dto.GlobalPayWithdrawDTO;
import cn.wizzer.app.user.modules.models.dto.XindovndWithdrawDTO;
import cn.wizzer.app.user.modules.models.enums.*;
import cn.wizzer.app.user.modules.models.vo.UserBankCardVO;
import cn.wizzer.app.user.modules.models.vo.UserWithdrawVO;
import cn.wizzer.app.user.modules.services.UserBankCardService;
import cn.wizzer.app.user.modules.services.UserService;
import cn.wizzer.app.user.modules.services.account.UserAccountRecordService;
import cn.wizzer.app.user.modules.services.account.UserWithdrawService;
import cn.wizzer.app.user.util.ElephantUtil;
import cn.wizzer.app.user.util.GlobalPayUtil;
import cn.wizzer.app.user.util.OneWalletUtil;
import cn.wizzer.app.user.util.XindovndUtil;
import cn.wizzer.framework.base.exceptions.BizException;
import cn.wizzer.framework.base.model.enums.BizExceptionEnum;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import cn.wizzer.framework.base.service.CommonDBService;
import cn.wizzer.framework.page.Pagination;
import cn.wizzer.framework.util.Id;
import cn.wizzer.framework.util.JSONUtil;
import cn.wizzer.framework.util.Result;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Charsets;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

@IocBean(args = {"refer:dao"})
@Service(interfaceClass = UserWithdrawService.class)
public class UserWithdrawServiceImpl extends BaseServiceImpl<UserWithdraw> implements UserWithdrawService {

    private static final Log log = Logs.get();

    public UserWithdrawServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private UserAccountRecordService recordService;

    @Inject
    private UserService userService;

    @Inject
    private CommonDBService dbService;

    @Inject
    @Reference(retries = 3, timeout = 10000)
    private SysMsgService userMsgService;

    @Inject
    @Reference
    private SysBankLibService bankLibService;

    @Inject
    @Reference
    private SysParamService sysParamService;

    @Inject
    @Reference
    private SysPayWayService sysPayWayService;

    @Inject
    @Reference
    private SysPayWayBankService sysPayWayBankService;

    @Inject
    private UserBankCardService userBankCardService;

    @Inject
    @Reference
    private SysCurrencyService sysCurrencyService;


    @Override
    public Pagination listPage(String conditionData, String currencyId, String status, String[] searchDate, String searchKeyword, WithdrawChannel channel, int pageNumber, int pageSize) {
        StringBuffer sb = new StringBuffer();
        sb.append("SELECT r.*,u.nickName,u.userName,u.phone,sc.name currencyName ,ubc.type FROM user_withdraw r " +
                "left join user u on u.id=r.userId " +
                "left join sys_currency sc on sc.id=r.currencyId " +
                "left join user_bank_card ubc on ubc.id = r.cardId ");
        sb.append(" where 1=1");
        if (Strings.isNotBlank(conditionData)) {
            String[] split = conditionData.split(",");
            for (String col : split) {
                String colName = UserColEnum.getValByName(col);
                if (Strings.isNotBlank(colName)) {
                    // 默认不显示游客、内部账号
                    sb.append(" and u.").append(colName).append(" = 0");
                }
            }
        }
        if (Strings.isNotBlank(searchKeyword)) {
            sb.append(" and ( u.userName = '").append(searchKeyword).append("'");
            sb.append(" or u.phone like '%").append(searchKeyword).append("%' ");
            sb.append(" or r.no = '").append(searchKeyword).append("' )");
        }
        if (searchDate != null && searchDate.length > 0) {
            sb.append(" and r.ctAt >=").append(Times.d2TS(Times.D(searchDate[0])));
            sb.append(" and r.ctAt <=").append(Times.d2TS(Times.D(searchDate[1])));
        }
        if (status != null) {
            sb.append(" and r.status = '").append(status).append("'");
        }
        if (Strings.isNotBlank(currencyId)) {
            sb.append(" and r.currencyId = '").append(currencyId).append("'");
        }
        if (channel != null) {
            sb.append(" and r.channel = '").append(channel).append("'");
        }
        sb.append(" order by r.ctAt desc");
        return dbService.listPage(UserWithdrawVO.class, pageNumber, pageSize, sb.toString());
    }

    @Override
    public Map<String, BigDecimal> searchSum(String conditionData, String currencyId, String status, String[] searchDate, String searchKeyword, WithdrawChannel channel) {

        StringBuffer sb = new StringBuffer();
        sb.append("select IFNULL(sum(r.amount * c.rate), 0)AS amount,");
        sb.append(" IFNULL(sum(r.realAmount * c.rate), 0) AS realAmount");
        sb.append(" from user_withdraw r left join sys_currency c on r.currencyId=c.id ");
        sb.append(" left join user u on u.id = r.userId ");
        sb.append(" where 1=1");
        if (Strings.isNotBlank(conditionData)) {
            String[] split = conditionData.split(",");
            for (String col : split) {
                String colName = UserColEnum.getValByName(col);
                if (Strings.isNotBlank(colName)) {
                    // 默认不显示游客、内部账号
                    sb.append(" and u.").append(colName).append(" = 0");
                }
            }
        }
        if (Strings.isNotBlank(searchKeyword)) {
            sb.append(" and ( u.userName = '").append(searchKeyword).append("'");
            sb.append(" or u.phone like '%").append(searchKeyword).append("%' ");
            sb.append(" or r.no = '").append(searchKeyword).append("' )");
        }
        if (searchDate != null && searchDate.length > 0) {
            sb.append(" and r.ctAt >=").append(Times.d2TS(Times.D(searchDate[0])));
            sb.append(" and r.ctAt <=").append(Times.d2TS(Times.D(searchDate[1])));
        }
        if (status != null) {
            sb.append(" and r.status = '").append(status).append("'");
        }
        if (Strings.isNotBlank(currencyId)) {
            sb.append(" and r.currencyId = '").append(currencyId).append("'");
        }
        if (channel != null) {
            sb.append(" and r.channel = '").append(channel).append("'");
        }

        Sql sql = Sqls.create(sb.toString());
        sql.setCallback(Sqls.callback.map());
        this.dao().execute(sql);
        UserWithdraw sum = sql.getObject(UserWithdraw.class);
        return Result.createMap("allAmount", sum.getAmount(), "allRealAmount", sum.getRealAmount());
    }

    @Aop(TransAop.REPEATABLE_READ)
    @Override
    public UserWithdraw add(WithdrawChannel channel, String currencyId, BigDecimal amount, String toUser, String cardId) throws Exception {

        SysCurrency currency = sysCurrencyService.fetch(currencyId);
        if (CurrencyConstant.THB.equals(currency.getName())) {
            if (new BigDecimal("100").compareTo(amount) > 0) {
                Result.breakWith(BizExceptionEnum.THB_WITHDRAW_MIN);
            }
            if (new BigDecimal("100000").compareTo(amount) < 0) {
                Result.breakWith(BizExceptionEnum.THB_WITHDRAW_MAX);
            }
        }
        if (CurrencyConstant.IDR.equals(currency.getName())) {
            if (new BigDecimal("10000").compareTo(amount) > 0) {
                Result.breakWith(BizExceptionEnum.IDR_WITHDRAW_MIN);
            }
            if (new BigDecimal("50000000").compareTo(amount) < 0) {
                Result.breakWith(BizExceptionEnum.IDR_WITHDRAW_MAX);
            }
        }
        //验证银行卡
        UserBankCardVO card = null;
        if (Strings.isNotBlank(cardId)) {
            userBankCardService.verifyBankCard(cardId, toUser);
            card = userBankCardService.getById(cardId);
        }
        //region 投注流水需要大于充值总额 的几倍,如果是后台扣款直接强制扣除
        if (channel == null) {
            User user = userService.fetch(toUser);
            // modify 20210325 根据用户流水限制额度来判断
            // 为了避免汇率变动的影响，USD取整处理
            if (user.getTransRestriction().setScale(0, BigDecimal.ROUND_DOWN).compareTo(user.getEffectQuota().setScale(0, BigDecimal.ROUND_DOWN)) > 0) {
                Result.breakWith(BizExceptionEnum.WITHDRAW_REJECT);
            }
        }


        //endregion


        //region 实际到账金额计算
        //本月申请转出次数
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.DATE, 1);
        int currentMouthTimes = this.count(Cnd.where("userId", "=", toUser)
                .and("status", "in", Result.createList(WithdrawStatusEnum.APPLYING, WithdrawStatusEnum.PENDING_BANK, WithdrawStatusEnum.SUCCESS))
                .and("ctAt", ">=", cal.getTime().getTime() / 1000)
        );
        BigDecimal realAmount = amount;
        User user = userService.fetch(toUser);
        //若超出次数则扣除手续费-百分之
        SysParam sysParam = sysParamService.fetch();
        if (currentMouthTimes >= user.getWithdrawFreeTimes()) {
            BigDecimal hundred = new BigDecimal(100);
            realAmount = realAmount.multiply(hundred.subtract(sysParam.getWithdrawFee())
                    .divide(hundred)).setScale(0, RoundingMode.DOWN);
        }
        //endregion
        //region insert
        UserWithdraw withdraw = new UserWithdraw();
        if (null != card) {
            withdraw.setCardNumber(card.getCardNumber());
            withdraw.setAccountName(card.getAccountName());
            withdraw.setBank(card.getBank());
            withdraw.setBankLibId(card.getBankLibId());
            withdraw.setCountry(card.getCountry());
            withdraw.setCardId(cardId);
        }
        withdraw.setAmount(amount);
        withdraw.setRealAmount(realAmount);
        withdraw.setCurrencyId(currencyId);
        withdraw.setUserId(toUser);
        withdraw.setStatus(WithdrawStatusEnum.APPLYING);
        withdraw.setChannel(channel);
        withdraw.setNo(Id.createOrderNo());
        this.insert(withdraw);


        recordService.add(currencyId, toUser, amount, BizTypeEnum.WITHDRAW, withdraw.getId(), withdraw.getNo());
        return withdraw;
    }

    @Aop(TransAop.REPEATABLE_READ)
    @Override
    public void manual(String currencyId, BigDecimal amount, String toUser, String cardId, String platformUid, WithdrawChannel channel) throws Exception {
        this.add(channel, currencyId, amount, toUser, cardId);
    }

    @Aop(TransAop.SERIALIZABLE)
    @Override
    public synchronized UserWithdraw examine(String id, String remark, Boolean sure, WithdrawChannel channel, String opBy, String requestUrl) throws Exception {
        log.info("witihdraw id:" + id + ",remark:" + remark + ",sure:" + sure + ",channel:" + channel + ",opBy" + opBy + ",requestUrl:" + requestUrl);
        UserWithdraw record = this.fetch(id);
        SysCurrency currency = sysCurrencyService.fetch(record.getCurrencyId());
        log.info("record：" + record);
        if (record.getStatus() == WithdrawStatusEnum.SUCCESS || record.getStatus() == WithdrawStatusEnum.FAIL) {
            throw new BizException(BizExceptionEnum.THE_ORDER_HAS_BEEN_OPERATED);
        }
        // if(通过){
        //      1、系统扣款，不需要银行，主要是调账用
        //      2、手动打款，自己银行卡转账
        //      3、壹钱包
        //      4、新都
        //      5、globalPay
        //  }
        // else{}取消/拒绝
        String title = "";
        String result = "";
        if (sure) {
            if (WithdrawChannel.PLATFORM.equals(channel)) {
                record.setTransferAt(Times.getTS());
                record.setStatus(WithdrawStatusEnum.SUCCESS);
                // 提现成功提醒
                title = " system deduction reminder";
                result = "successful";
            } else if (WithdrawChannel.BANK.equals(channel)) {
                record.setTransferAt(Times.getTS());
                record.setStatus(WithdrawStatusEnum.SUCCESS);
                // 提现成功提醒
                title = " Successful withdrawal reminder";
                result = "successful";
            } else if (WithdrawChannel.ONE_WALLET.equals(channel)) {
                // 1、请求壹钱包 return
                // 2、壹钱包回调
                if (WithdrawStatusEnum.APPLYING.equals(record.getStatus())) {
                    SysPayWay payWay = sysPayWayService.fetch(
                            Cnd.where("currencyId", "=", record.getCurrencyId())
                                    .and("payWay", "=", channel)
                    );
                    //verify payWay
                    if (payWay.isDisabled()) {
                        Result.breakWith(BizExceptionEnum.PAY_WAY_DISABLED);
                    }
                    if (null != payWay.getMinWithDrawAmount() && record.getAmount().compareTo(payWay.getMinWithDrawAmount()) < 0) {
                        Result.breakWith(BizExceptionEnum.LESS_THAN_MIN_AMOUNT);
                    }
                    if (null != payWay.getMaxWithDrawAmount() && record.getAmount().compareTo(payWay.getMaxWithDrawAmount()) > 0) {
                        Result.breakWith(BizExceptionEnum.MORE_THAN_MAX_AMOUNT);
                    }
                    BigDecimal realAmount = record.getRealAmount();
                    SysParam sysParam = sysParamService.fetch();
                    String bankLibId = userBankCardService.fetch(record.getCardId()).getBankLibId();
                    SysPayWayBank sysPayWayBank = sysPayWayBankService.query(bankLibId, channel.toString(), record.getCurrencyId());
                    String respBody = OneWalletUtil.send(sysParam.getOneWalletToWithDrawUrl(),
                            OneWalletUtil.parseWithDrawParam(sysPayWayBank.getCode(), record.getCardNumber(),
                                    record.getAccountName(), record.getNo(), realAmount,
                                    OneWalletUtil.randomByte(), sysParam.getOneWalletSafeCode(),
                                    sysParam.getOneWalletWithDrawNotifyUrl()));
                    respBody = OneWalletUtil.decrypt(respBody, sysParam);
                    JSONObject jsonObject = JSONUtil.parseToJSON(respBody).getJSONObject("data");
                    //记录支付单号
                    record.setOutNo(jsonObject.getString("order_id"));
                    record.setStatus(WithdrawStatusEnum.PENDING_BANK);
                    record.setChannel(channel);
                    this.update(record);
                    return record;
                } else {
                    record.setTransferAt(Times.getTS());
                    record.setStatus(WithdrawStatusEnum.SUCCESS);
                    // 提现成功提醒
                    title = " Successful withdrawal reminder";
                    result = "successful";
                }
            } else if (WithdrawChannel.XINDO_VND.equals(channel)) {
                // 1、请求新都
                // 2、新都回调
                if (WithdrawStatusEnum.APPLYING.equals(record.getStatus())) {
                    if (CurrencyConstant.IDR.equals(currency.getName())) {
                        Result.breakWith("你是要死呀，印尼盾走新都代付");
                    }
                    SysPayWay payWay = sysPayWayService.fetch(
                            Cnd.where("currencyId", "=", record.getCurrencyId())
                                    .and("payWay", "=", channel)
                    );
                    //verify payWay
                    if (payWay.isDisabled()) {
                        Result.breakWith(BizExceptionEnum.PAY_WAY_DISABLED);
                    }
                    if (null != payWay.getMinWithDrawAmount() && record.getAmount().compareTo(payWay.getMinWithDrawAmount()) < 0) {
                        Result.breakWith(BizExceptionEnum.LESS_THAN_MIN_AMOUNT);
                    }
                    if (null != payWay.getMaxWithDrawAmount() && record.getAmount().compareTo(payWay.getMaxWithDrawAmount()) > 0) {
                        Result.breakWith(BizExceptionEnum.MORE_THAN_MAX_AMOUNT);
                    }
                    BigDecimal realAmount = record.getRealAmount();
                    SysParam sysParam = sysParamService.fetch();
                    //xindovnd.withdraw
                    String bankLibId = userBankCardService.fetch(record.getCardId()).getBankLibId();
                    SysPayWayBank sysPayWayBank = sysPayWayBankService.query(bankLibId, channel.toString(), record.getCurrencyId());
                    String url = sysParam.getXindovndToWithdrawUrl();
                    String notifyUrl = PayNotifyConstant.XIN_WITHDRAW_NOTIFY_COM;
                    if (requestUrl.contains(PayNotifyConstant.REQUEST_URL_NET)) {
                        notifyUrl = PayNotifyConstant.XIN_WITHDRAW_NOTIFY_NET;
                    }
                    Map<String, Object> withdrawParam = XindovndUtil.createWithdrawParam(record.getRealAmount().toString(),
                            sysParam.getXindovndMerchant(),
                            sysPayWayBank.getCode(),
                            record.getCardNumber(),
                            record.getAccountName(),
                            notifyUrl,
                            record.getNo(),
                            sysParam.getXindovndSecret());
                    String respBody = XindovndUtil.send(url, withdrawParam);
                    JSONObject jsonObject = JSONUtil.parseToJSON(respBody);
                    //验证状态
                    if (!jsonObject.getString("results").equals("success") || !jsonObject.getInteger("code").equals(0)) {
                        Result.breakWith(jsonObject.getString("results"));
                    }
                    record.setStatus(WithdrawStatusEnum.PENDING_BANK);
                    record.setChannel(channel);
                    this.update(record);
                    return record;
                } else {
                    record.setTransferAt(Times.getTS());
                    record.setStatus(WithdrawStatusEnum.SUCCESS);
                    // 先用opby这个字段代替传过来，当成第三方单号使用 20210115 懒得加参数
                    record.setOutNo(opBy);
                    // 提现成功提醒
                    title = " Successful withdrawal reminder";
                    result = "successful";
                }
            } else if (WithdrawChannel.ELEPHANT168.equals(channel)) {
                // 1、请求大象
                // 2、大象回调
                if (WithdrawStatusEnum.APPLYING.equals(record.getStatus())) {
                    if (CurrencyConstant.IDR.equals(currency.getName())) {
                        Result.breakWith("你是要死呀，印尼盾走大象代付");
                    }
                    SysPayWay payWay = sysPayWayService.fetch(
                            Cnd.where("currencyId", "=", record.getCurrencyId())
                                    .and("payWay", "=", channel)
                    );
                    //verify payWay
                    if (payWay.isDisabled()) {
                        Result.breakWith(BizExceptionEnum.PAY_WAY_DISABLED);
                    }
                    if (null != payWay.getMinWithDrawAmount() && record.getAmount().compareTo(payWay.getMinWithDrawAmount()) < 0) {
                        Result.breakWith(BizExceptionEnum.LESS_THAN_MIN_AMOUNT);
                    }
                    if (null != payWay.getMaxWithDrawAmount() && record.getAmount().compareTo(payWay.getMaxWithDrawAmount()) > 0) {
                        Result.breakWith(BizExceptionEnum.MORE_THAN_MAX_AMOUNT);
                    }
                    SysParam sysParam = sysParamService.fetch();
                    //elephant.withdraw
                    String bankLibId = userBankCardService.fetch(record.getCardId()).getBankLibId();
                    SysPayWayBank sysPayWayBank = sysPayWayBankService.query(bankLibId, channel.toString(), record.getCurrencyId());
                    String url = sysParam.getElephantToWithdrawUrl();
                    String notifyUrl = PayNotifyConstant.ELE_WITHDRAW_NOTIFY_COM;
                    if (requestUrl.contains(PayNotifyConstant.REQUEST_URL_NET)) {
                        notifyUrl = PayNotifyConstant.ELE_WITHDRAW_NOTIFY_NET;
                    }
                    Map<String, Object> withdrawParam = ElephantUtil.createWithdrawParam(record.getRealAmount().toString(),
                            sysParam.getElephantMerchant(),
                            sysPayWayBank.getCode(),
                            record.getCardNumber(),
                            record.getAccountName(),
                            notifyUrl,
                            record.getNo(),
                            sysParam.getElephantSecret());
                    String respBody = ElephantUtil.send(url, withdrawParam);
                    JSONObject jsonObject = JSONUtil.parseToJSON(respBody);
                    //验证状态
                    if (!jsonObject.getString("results").equals("success") || !jsonObject.getInteger("code").equals(0)) {
                        Result.breakWith(jsonObject.getString("results"));
                    }
                    record.setStatus(WithdrawStatusEnum.PENDING_BANK);
                    record.setChannel(channel);
                    this.update(record);
                    return record;
                } else {
                    record.setTransferAt(Times.getTS());
                    record.setStatus(WithdrawStatusEnum.SUCCESS);
                    // 先用opby这个字段代替传过来，当成第三方单号使用 20210115 懒得加参数
                    record.setOutNo(opBy);
                    // 提现成功提醒
                    title = " Successful withdrawal reminder";
                    result = "successful";
                }
            } else if (WithdrawChannel.GLOBALPAY.equals(channel)) {
                // 1、请求
                // 2、回调
                if (WithdrawStatusEnum.APPLYING.equals(record.getStatus())) {
                    SysPayWay payWay = sysPayWayService.fetch(
                            Cnd.where("currencyId", "=", record.getCurrencyId())
                                    .and("payWay", "=", channel)
                    );
                    //verify payWay
                    if (payWay.isDisabled()) {
                        Result.breakWith(BizExceptionEnum.PAY_WAY_DISABLED);
                    }
                    if (null != payWay.getMinWithDrawAmount() && record.getAmount().compareTo(payWay.getMinWithDrawAmount()) < 0) {
                        Result.breakWith(BizExceptionEnum.LESS_THAN_MIN_AMOUNT);
                    }
                    if (null != payWay.getMaxWithDrawAmount() && record.getAmount().compareTo(payWay.getMaxWithDrawAmount()) > 0) {
                        Result.breakWith(BizExceptionEnum.MORE_THAN_MAX_AMOUNT);
                    }
                    SysParam sysParam = sysParamService.fetch();

                    UserBankCard userBankCard = userBankCardService.fetch(record.getCardId());
                    // 印度用户使用UPI提现时，bankCode字段传UPI
                    String bankCode = "";
                    if (BankCardTypeEnum.BANKCARD.equals(userBankCard.getType())) {
                        String bankLibId = userBankCard.getBankLibId();
                        SysPayWayBank sysPayWayBank = sysPayWayBankService.query(bankLibId, channel.toString(), record.getCurrencyId());
                        bankCode = sysPayWayBank.getCode();
                    } else if (BankCardTypeEnum.UPI.equals(userBankCard.getType())) {
                        bankCode = BankCardTypeEnum.UPI.getNAME();
                    }
                    String url = sysParam.getGlobalPayToWithdrawUrl();
                    // TMD GLOBAL
//                    String notifyUrl = PayNotifyConstant.GLO_WITHDRAW_NOTIFY_COM;
//                    if (requestUrl.contains(PayNotifyConstant.REQUEST_URL_NET)) {
//                        notifyUrl = PayNotifyConstant.GLO_WITHDRAW_NOTIFY_NET;
//                    }
                    String notifyUrl = PayNotifyConstant.GLO_WITHDRAW_NOTIFY_NET;

                    Map<String, Object> withdrawParam = GlobalPayUtil.createWithdrawParam(
                            record.getNo(),
                            record.getCardNumber(),
                            record.getAccountName(),
                            currency.getName(),
                            record.getRealAmount().setScale(0, RoundingMode.DOWN).toString(),
                            bankCode,
                            userBankCard.getPhone(),
                            // 印度普通银行卡提现province字段需传IFSCCODE
                            Strings.isNotBlank(userBankCard.getIFSCCODE()) ? userBankCard.getIFSCCODE() : "",
                            notifyUrl,
                            "sum" // 必填 传个 sum给接口方
                    );
                    String respBody = GlobalPayUtil.send(url, withdrawParam);
                    JSONObject jsonObject = JSONUtil.parseToJSON(respBody);
                    //验证状态
                    if ("FAIL".equals(jsonObject.getString("status"))) {
                        Result.breakWith(jsonObject.getString("err_msg"));
                    }
                    record.setStatus(WithdrawStatusEnum.PENDING_BANK);
                    record.setChannel(channel);
                    this.update(record);
                    return record;
                } else {
                    record.setTransferAt(Times.getTS());
                    record.setStatus(WithdrawStatusEnum.SUCCESS);
                    // 先用opby这个字段代替传过来，当成第三方单号使用 20210115 懒得加参数
                    record.setOutNo(opBy);
                    // 提现成功提醒
                    title = " Successful withdrawal reminder";
                    result = "successful";
                }
            }
        } else {
            //失败
            record.setStatus(WithdrawStatusEnum.FAIL);
            // 提现失败通知
            // 金额返还到余额
            recordService.add(record.getCurrencyId(), record.getUserId(), record.getAmount(), BizTypeEnum.WITHDRAW_REJECT, id, record.getNo());
            title = "Notice of withdrawal failure";
            result = "failed";
        }

        record.setOpBy(opBy);
        record.setOpAt(Times.getTS());
        record.setExamineAt(Times.getTS());
        record.setRemark(remark);
        record.setChannel(channel);
        this.update(record);

        //构建消息通知
        SysMsg userMsg = userMsgService.createMsg("member", title, String.format("<p>" + result + "，Order Number<span style='color:red'>[%s]</span>，" +
                        "Withdraw amount<span style='color:red'>[%s]</span>",
                record.getNo(), record.getAmount()), opBy);
        userMsgService.saveMsg(userMsg, new String[]
                {
                        record.getUserId()
                });
        return record;
    }

    @Override
    public UserWithdrawVO detail(String id) {
        log.info("detail id:" + id);
        Sql sql = Sqls.create("SELECT uc.*, sc.name currencyName ,u.nickName, u.email " +
                "FROM user_withdraw uc " +
                "LEFT JOIN user u ON uc.userId = u.id " +
                "LEFT JOIN sys_currency sc ON sc.id = uc.currencyId " +
                "WHERE uc.id = @id");
        sql.setParam("id", id);
        UserWithdrawVO userWithdrawVO = dbService.fetchSql(UserWithdrawVO.class, sql.toString());
        List<UserWithdrawVO.WithdrawStep> list = Result.createList(
                UserWithdrawVO.createStep().setStatus(WithdrawStatusEnum.APPLYING).setAt(userWithdrawVO.getCtAt()),
                UserWithdrawVO.createStep().setStatus(WithdrawStatusEnum.PENDING_BANK).setAt(userWithdrawVO.getExamineAt()),
                userWithdrawVO.getStatus().equals(WithdrawStatusEnum.FAIL) ?
                        UserWithdrawVO.createStep().setStatus(WithdrawStatusEnum.FAIL).setAt(userWithdrawVO.getExamineAt())
                        : UserWithdrawVO.createStep().setStatus(WithdrawStatusEnum.SUCCESS).setAt(userWithdrawVO.getTransferAt())
        );
        //已取消的单
        if (userWithdrawVO.getCancelAt() != null) {
            for (int i = 0; i < list.size(); i++) {
                //按流程某条内容为空时则插前面
                if (list.get(i + 1) != null && (list.get(i + 1).getAt() == null)) {
                    list.add(i + 1, UserWithdrawVO.createStep().setAt(userWithdrawVO.getCancelAt()).setStatus(WithdrawStatusEnum.CANCELLED));
                    list = list.subList(0, i + 2);
                    break;
                }
            }
        }
        userWithdrawVO.setSteps(list);
        // remark字段不展示
        userWithdrawVO.setRemark("");
        return userWithdrawVO;
    }

    @Override
    public void cancel(String id, String remark, String platformUid) {
        UserWithdraw userWithdraw = new UserWithdraw();
        userWithdraw.setId(id);
        userWithdraw.setCancelAt(Times.getTS());
        userWithdraw.setStatus(WithdrawStatusEnum.CANCELLED);
        userWithdraw.setOpAt(Times.getTS());
        userWithdraw.setRemark(remark);
        userWithdraw.setOpBy(platformUid);
        this.updateIgnoreNull(userWithdraw);
    }

    @Override
    public String oneWalletNotify(String respBody) throws Exception {
        SysParam sysParam = sysParamService.fetch();
        try {
            String decrypt = OneWalletUtil.decrypt(respBody, sysParam);
            JSONObject jsonObject = JSONUtil.parseToJSON(decrypt);
            BigDecimal paidAmount = jsonObject.getBigDecimal("paid_amount");
            String no = jsonObject.getString("sh_order_no");
            Integer orderStatus = jsonObject.getInteger("order_status");
            UserWithdraw fetch = this.fetch(Cnd.where("no", "=", no));
            if (fetch.getRealAmount().compareTo(paidAmount) == 0 && orderStatus.equals(3)) {
                this.examine(fetch.getId(), null, true, WithdrawChannel.ONE_WALLET, "OneWalletNotify", "");
            } else {
                this.examine(fetch.getId(), null, false, WithdrawChannel.ONE_WALLET, "OneWalletNotify", "");
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }

        return OneWalletUtil.success(sysParam.getOneWalletSafeCode());
    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public UserWithdraw xindovndNotify(XindovndWithdrawDTO xin) throws Exception {
        log.info("xindovndNotify:" + xin);
        if (XindovndUtil.verifyWithdrawNotify(xin, sysParamService.fetch().getXindovndSecret())) {
            UserWithdraw fetch = this.fetch(Cnd.where("no", "=", xin.getOuttransferno()));
            //完成状态
            String remark = xin.getRemark() == null ? null : URLDecoder.decode(xin.getRemark(), Charsets.UTF_8);
            if ("1".equals(xin.getStatus())) {
                fetch = this.examine(fetch.getId(), remark, true, WithdrawChannel.XINDO_VND, xin.getTransferno(), "");
            }
            //撤销状态
            else if ("4".equals(xin.getStatus())) {
                fetch = this.examine(fetch.getId(), remark, false, WithdrawChannel.XINDO_VND, "XindovandNotify", "");
            }
            return fetch;
            // 看不要不要整理一下，这狗日的代码，第三方单号
//            this.update(Chain.make("transferAt",
//                    new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(xin.getEndtime()).getTime() / 1000L)
//                            .add("outNo", xin.getTransferno()),
//                    Cnd.where("id", "=", fetch.getId())
//            );
        } else {
            log.error("xindovndNotify withdraw fail");
            Result.breakWith("fail");
            return null;
        }
    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public UserWithdraw elephantNotify(ElephantWithdrawDTO elephant) throws Exception {
        log.info(" elephantNotify:" + elephant);
        if (ElephantUtil.verifyWithdrawNotify(elephant, sysParamService.fetch().getElephantSecret())) {
            UserWithdraw fetch = this.fetch(Cnd.where("no", "=", elephant.getOuttransferno()));
            //完成状态
            String remark = elephant.getRemark() == null ? null : URLDecoder.decode(elephant.getRemark(), Charsets.UTF_8);
            if ("1".equals(elephant.getStatus())) {
                fetch = this.examine(fetch.getId(), remark, true, WithdrawChannel.ELEPHANT168, elephant.getTransferno(), "");
            }
            //撤销状态
            else if ("4".equals(elephant.getStatus())) {
                fetch = this.examine(fetch.getId(), remark, false, WithdrawChannel.ELEPHANT168, " elephantNotify", "");
            }
            return fetch;
            // 看不要不要整理一下，这狗日的代码，第三方单号
//            this.update(Chain.make("transferAt",
//                    new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse( elephant.getEndtime()).getTime() / 1000L)
//                            .add("outNo",  elephant.getTransferno()),
//                    Cnd.where("id", "=", fetch.getId())
//            );
        } else {
            log.error(" elephantNotify withdraw fail");
            Result.breakWith("fail");
            return null;
        }
    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public UserWithdraw globalPayNotify(GlobalPayWithdrawDTO globalPay) throws Exception {
        log.info("globalPayNotify:" + globalPay);
        String secret = GlobalPayParamConstant.IDR_SECRET;
        if (GlobalPayParamConstant.THB_MER_NO.equals(globalPay.getMer_no())) {
            secret = GlobalPayParamConstant.THB_SECRET;
        } else if (GlobalPayParamConstant.INR_MER_NO.equals(globalPay.getMer_no())) {
            secret = GlobalPayParamConstant.INR_SECRET;
        }
        if (GlobalPayUtil.verifyWithdrawNotify(globalPay, secret)) {
            UserWithdraw fetch = this.fetch(Cnd.where("no", "=", globalPay.getMer_order_no()));
            String remark = globalPay.getErr_code() + globalPay.getErr_msg();
            //完成状态
            if ("SUCCESS".equals(globalPay.getStatus())) {
                fetch = this.examine(fetch.getId(), remark, true, WithdrawChannel.GLOBALPAY, globalPay.getOrder_no(), "");
            }
            //撤销状态
            else {
                fetch = this.examine(fetch.getId(), remark, false, WithdrawChannel.GLOBALPAY, " globalPayNotify", "");
            }
            return fetch;
        } else {
            log.error("globalPayNotify withdraw fail,globalPayDTO:" + globalPay);
            Result.breakWith("fail");
            return null;
        }
    }

    /**
     * 获取提现方式
     *
     * @param bankLibId 银行id
     * @return
     */
    @Override
    public List<String> getWithdrawalMethod(String bankLibId) {
        Sql sql = Sqls.create("SELECT pw.payWay " +
                "FROM user_withdraw r " +
                "LEFT JOIN sys_pay_way_bank pb ON pb.bankLibId = r.bankLibId AND pb.uses IN ( 'ONLY_WITHDRAW', 'R_W' ) AND pb.disabled = 0 " +
                "LEFT JOIN sys_pay_way AS pw ON pw.id = pb.payWayId AND pw.currencyId = r.currencyId $condition ");
        Cnd cnd = Cnd.where("r.bankLibId", "=", bankLibId);
        sql.setCondition(cnd).appendSourceSql("GROUP BY pw.payWay");

        // 将放回值组合成字符串集合返回
        List<SysPayWay> list = dbService.list(SysPayWay.class, sql.toString());
        List<String> stringList = new ArrayList<>();
        for (SysPayWay o : list) {
            stringList.add(o.getPayWay());
        }

        return stringList;
    }
}
